/*
Copyright (c) 2006 - 2008  Universitaet Osnabrueck, virtUOS
Authors: Markus Ketterl, Johannes Emden, Nils Schuhmacher, Andreas Rickling

This file is part of virtPresenter.

virtPresenter is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

virtPresenter is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with virtPresenter.  If not, see <http://www.gnu.org/licenses/>.
*/

package virtPresenter.model
{
	import flash.events.EventDispatcher;
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.events.IOErrorEvent;

	[Bindable]
	public class AdvancedXMLParser extends EventDispatcher
	{
		private var timeStepXML_url:String;
		private var timeStepXML_Loader:URLLoader;
		
		private var logicXML_url:String;
		private var logicXML_Loader:URLLoader;
		
		private var mouseXML_url:String;
		private var mouseXML_Loader:URLLoader;
		
		private var mouseSteps:Array;
		private var timeSteps:Array;
		private var slides:Array;
		
		private var locator : virtPresenterLocator = virtPresenterLocator.getInstance();
		
		public var lectureSource:String;
		public var lectureTitle:String;
		
		private var ready:Boolean=false;
		
		private var mouseXMLAvailable:Boolean = false;
		
		public function AdvancedXMLParser(lectureSource:String,lectureTitle:String)
		{
			this.lectureSource=lectureSource;
			this.lectureTitle=lectureTitle;
			timeStepXML_url = "lectures/"+lectureSource+"/data/logXML.xml";           
			logicXML_url = "lectures/"+lectureSource+"/data/search.xml";

			
			// loading timeStepXML
			var xmlURL:URLRequest = new URLRequest(timeStepXML_url);
		    timeStepXML_Loader = new URLLoader(xmlURL);
			timeStepXML_Loader.addEventListener("complete", timeStepXML_loaded);
			
			// loading mouse XML
			mouseXML_url = "lectures/"+lectureSource+"/data/mouse.xml";
			var mouseXMLRequest:URLRequest = new URLRequest(mouseXML_url);
			mouseXML_Loader = new URLLoader(mouseXMLRequest);
			mouseXML_Loader.addEventListener("complete", mouseXML_loaded); //mouse.xml available: success
			mouseXML_Loader.addEventListener(IOErrorEvent.IO_ERROR, mouseXML_fault); //mouse.xml unavailable: fault
		}
		
		/* 
		 * #1
		 */
		private function timeStepXML_loaded(e:Event):void
		{
			// loading logicXML
			var xmlURL:URLRequest = new URLRequest(logicXML_url);
		    logicXML_Loader = new URLLoader(xmlURL);
			logicXML_Loader.addEventListener("complete", xmlFiles_loaded);
		}
		
		/* 
		 * #2
		 */
		private function xmlFiles_loaded(e:Event):void
		{
			if(parse())
			{
				extractSlides();
				this.ready=true;
			}
	/*		
			for(var i:int=0; i<timeSteps.length;i++)
			{
				var tmp:TimeStep = timeSteps[i];
				trace(tmp);
			}	
*/
		}
		
		/* 
		 * #MouseXML loaded
		 */
		private function mouseXML_loaded(e:Event):void
		{
			trace("MouseStep success");
			var mouseXML:XML = XML(mouseXML_Loader.data);
			mouseSteps = new Array(); // Erstelle ein leeres Array
			
			for each (var koordinaten:XML in mouseXML.koordinaten)
			{
				var tmp:MouseStep = new MouseStep();
				
				tmp.time = int(koordinaten.@time.toString());
				tmp.x = int(koordinaten.@x.toString());
				tmp.y = int(koordinaten.@y.toString());

				mouseSteps.push(tmp);	
			}
			
			this.mouseXMLAvailable=true;
			
			/*
			for(var i:int=0; i<mouseSteps.length;i++)
			{
				var tmpStep:MouseStep = mouseSteps[i];
				trace(tmpStep);
			}
			*/		
		}
		
		/* 
		 * #MouseXML loaded
		 */
		private function mouseXML_fault(e:Event):void
		{
			this.mouseXMLAvailable=false;	
			//trace("AdvancedScrubber. MouseStep not available");
		}
		
		/* Diese Funktion definiert, wie Folien sortiert werden sollen.
		 */
		private function sortOnTime(a:TimeStep, b:TimeStep):Number
		{
		    var aTimeStep:Number = a.getTime();
		    var bTimeStep:Number = b.getTime();
		
		    if(aTimeStep > bTimeStep) return 1;
		    else if(aTimeStep < bTimeStep) return -1;
		    else return 0; //aSlide == bSlide
		}
		
		private function parse():Boolean
		{
			if(parseTimeStepXML())
			{
				if(parseLogicXML())return true;
			}
			
			return false;
		}
		
		private function parseTimeStepXML():Boolean
		{
			var xmlTimeSteps:XML = XML(timeStepXML_Loader.data);
			timeSteps = new Array(); // Erstelle ein leeres Array
			
			for each (var timeStep:XML in xmlTimeSteps.Event)
			{
				var tmp:TimeStep = new TimeStep();
				tmp.setTime(int(timeStep.@Time.toString()));
				tmp.setStep(int(timeStep.@Step.toString()));

				timeSteps.push(tmp);	
			}
			
			/* WICHTIG: Die Folien müssen nach ihrem Zeitstempel sortiert sein,
			 *          damit nach ihnen gesucht werden kann.
			 */
			timeSteps.sort(sortOnTime);			
			
			return true;
		}
		
		private function parseLogicXML():Boolean
		{
			var xmlLogic:XML = XML(logicXML_Loader.data);
			for each (var slide:XML in xmlLogic.slide)
			{
				var id:int=int(slide.@ID.toString());
				var startStep:int=int(slide.@startStep.toString());
				var endStep:int=int(slide.@endStep.toString());
				var title:String = slide.title.toString();
				var textOnSlide:String = slide.textOnSlide.toString();

				slides = new Array(); // Erstelle ein leeres Array
				
				for(var i:int=0; i<timeSteps.length;i++)
				{
					var tmp:TimeStep = timeSteps[i];
					if(tmp.getStep()>=startStep && tmp.getStep()<=endStep)
					{
						tmp.setID(id);	
						tmp.setTitle(title);
																					
						//Muss +1 nicht weg, denn es gibt auch Folien, die keine Animationsschritte haben	
						//tmp.setAnimationStep(tmp.getStep()-startStep+1);
						tmp.setAnimationStep(tmp.getStep()-startStep+1);
						
						// Nur der erste Timestep einer Folien bekommt den text; 
						if(tmp.getStep()==startStep)
						{
							tmp.setTextOnSlide(textOnSlide);
						}			
					}
				}
			}
				
			
			
						
			return parseLogicXMLAnimationStep();
		}
		
		
		
		private function parseLogicXMLAnimationStep():Boolean
		{
			var xmlLogic:XML = XML(logicXML_Loader.data);
			for each (var animationSequence:XML in xmlLogic.slide.animations.animationSequence)
			{
			
				for each (var animation:XML in animationSequence.animation)
				{
					var step:int = int(animationSequence.@step.toString());
					var text:String = animation.toString();
					if(text!=null)
						addTextToTimeStep(step,text);
					//trace(step+": "+text);
				}
			
			}
			
			return true;
		}
		
		// Wenns mal wieder etwas länger dauert: Performance steht hier nicht im Mittelpunkt
		private function addTextToTimeStep(timestep:int, text:String):void
		{
			for(var i:int=0; i<timeSteps.length;i++)
			{
				var tmp:TimeStep = timeSteps[i];
				if(timestep==tmp.getStep())
				{

					tmp.setTextOnSlide(tmp.getTextOnSlide()+" "+text);
				}	
			}	
		}
		
		private var lastJ:int=0;
		
		public function getTimeStepByTime(time:int):TimeStep
		{
			/* Diese Funktion gibt die Folie zurück, deren Zeitstempel gefragt ist.
			 * Ist der Zeitstempel vor dem Zeitstempel der ersten Folie, wird die
			 * erste Folie zurück gegeben.
			 */
			var found:TimeStep = timeSteps[0]; 
			for(var j:int=0;j<timeSteps.length;j++)
			{
				var pos:int=(lastJ+j)%timeSteps.length;
				var tmp:TimeStep = timeSteps[pos];
				var tmpNext:TimeStep =timeSteps[(lastJ+j+1)%timeSteps.length];
				if(tmp.getTime()<=time && time<tmpNext.getTime()){
					found=tmp;
					lastJ=pos;
					return found;
				}
			}
			
		    // Sonderfall 1. Folie:
		    var first:TimeStep = timeSteps[0]; 
			if(time<first.getTime()) return first;
			else // Sonderfall letzte Folie
				return timeSteps[timeSteps.length-1];
			
			/*
			for(var j:int=0;j<timeSteps.length;j++)
			{
				trace(j);
				var tmp:TimeStep = timeSteps[j];
				if(tmp.getTime()<=time) found=tmp;
				else break;
			}	
			*/	
			return found;
		}
		
		public function getTimeSteps():Array
		{
			return this.timeSteps;
		}
		
		public function getSlides():Array
		{
			return this.slides;
		}
		
		/*
		 * Gibt die vorherige Folie. Für Previous-Funktion
		 */
		public function getPreviousSlideStep(time:int):TimeStep
		{
			

			var found:TimeStep=slides[0]; // vor erstem Step kommt wieder der erste Step (by Design)

			if(getTimeStepByTime(time).getAnimationStep()>0)
			{
				for(var j:int=1;j<slides.length;j++) 
				{
					var tmp:TimeStep = slides[j];
					if(tmp.getTime()<=time) found=slides[j];
					else break;
				}
			}
			else
			{
				for(var k:int=1;k<slides.length;k++) 
				{
					var tmp2:TimeStep = slides[k];
					if(tmp2.getTime()<=time) found=slides[k-1];
					else break;
				}
			} 
			return found;
		}
		
		/*
		 * Gibt den vorherigen Animationsschritt. Für Previous-Funktion
		 */		
		public function getPreviousStep(time:int):TimeStep
		{
			var found:TimeStep = timeSteps[0]; // vor erstem Step kommt wieder der erste Step (by Design)
			for(var j:int=1;j<timeSteps.length;j++) 
			{
				var tmp:TimeStep = timeSteps[j];
				if(tmp.getTime()<=time) found=timeSteps[j-1];
				else break;
			}
			return found; 
		}
		
		
		/*
		 * Gibt den nächsten Step. Für Advance-Funktion
		 */
		public function getNextStep(time:int):TimeStep
		{
			for(var j:int=0;j<timeSteps.length-1;j++)
			{
				var tmp:TimeStep = timeSteps[j];
				if(tmp.getTime()>time) return tmp;
			}
			return 	timeSteps[timeSteps.length-1]; // nach letztem Step kommt wieder der letzte Step (by Design)
		}
		
		/*
		 * Gibt den Step der nächsten Folie. Für Advance-Funktion
		 */
		public function getNextSlideStep(time:int):TimeStep
		{
			for(var j:int=0;j<slides.length-1;j++)
			{
				var tmp:TimeStep = slides[j];
				if(tmp.getTime()>time) return tmp;
			}
			return 	slides[slides.length-1]; // nach letztem Step kommt wieder der letzte Step (by Design)
		}
		
		
		public function extractSlides():void
		{
			var last:int =-1;
			slides = new Array(); // Erstelle ein leeres Array
			for(var j:int=0;j<timeSteps.length;j++) 
			{
				var tmp:TimeStep = timeSteps[j];
				if(tmp.getID()!=last)
				{
					slides.push(tmp);
					last=tmp.getID();
				}
			}
		}
		
		public function isReady():Boolean
		{
			return this.ready;
		}
		
	}
}